﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MyLibrary.UI
{
    /// <summary>
    /// 类似MSN的弹出框
    /// </summary>
    public partial class MSNNotifier : Form
    {
        public MSNNotifier()
        {
            ShowInTaskbar = MaximizeBox = MinimizeBox = ControlBox = false;
            TopMost = true;
            FormBorderStyle = FormBorderStyle.None;
            WindowState = FormWindowState.Minimized;
            base.Show();
            base.Hide();
            WindowState = FormWindowState.Normal;

            this.BackgroupBitmap = MyLibrary.UI.Properties.Resources.skin;
            this.CloseImage = MyLibrary.UI.Properties.Resources.close;

            timer.Enabled = true;
            timer.Tick += new EventHandler(this.TimerCallback);
        }

        #region 私有变量
        protected Bitmap _background = null;
        protected Bitmap _closeIcon = null;
        private Point _closeIconLocation = new Point(280, 57);
        protected Size _closeIconSize;  
        protected Rectangle _realTitleRectangle;
        protected Rectangle _realContentRectangle;
        protected Rectangle _workAradRectangle;
        protected Timer timer = new Timer();    //显示计时器
        protected MSNNotifierStates _notifierState = MSNNotifierStates.Hidden;
        protected string _title = "无标题"; //标题
        protected string _content = "无内容"; //内容
        protected Color _normalTitleColor = Color.FromArgb(255, 0, 0);
        protected Color _hoverTitleColor = Color.FromArgb(255, 0, 0);
        protected Color _normalContentColor = Color.FromArgb(0, 0, 0);
        protected Color _hoverContentColor = Color.FromArgb(0, 0, 0x66);
        private Color _transparencyColor = Color.FromArgb(255, 0, 255);
        protected Font _normalTitleFont = new Font("Arial", 12, FontStyle.Regular, GraphicsUnit.Pixel);
        protected Font _hoverTitleFont = new Font("Arial", 12, FontStyle.Bold, GraphicsUnit.Pixel);
        protected Font _normalContentFont = new Font("Arial", 11, FontStyle.Regular, GraphicsUnit.Pixel);
        protected Font _hoverContentFont = new Font("Arial", 11, FontStyle.Bold, GraphicsUnit.Pixel);

        int _timeToShow = 500;
        int _timeToStay = 3000;
        int _timeToHide = 500;

        protected int _showEvents;
        protected int _hideEvents;
        protected int _visibleEvents;
        protected int _incrementShow;
        protected int _incrementHide;

        protected bool _isMouseOverPopup = false;
        protected bool _isMouseOverClose = false;
        protected bool _isMouseOverContent = false;
        protected bool _isMouseOverTitle = false;
        protected bool _isMouseDown = false;
        protected bool _keepVisibleOnMouseOver = true;
        protected bool _reShowOnMouseOver = true;
        protected bool _titleClickable = false;
        protected bool _contentClickable = true;
        protected bool _closeClickable = true;
        protected bool _enableSelectionRectangle = true;
        private Rectangle _titleRectangle = new Rectangle(150, 57, 125, 28);
        private Rectangle _contentRectangle = new Rectangle(75, 92, 215, 55);
        #endregion

        #region 事件
        /// <summary>
        /// 当关闭按钮点击时
        /// </summary>
        public event EventHandler OnCloseClick = null;
        /// <summary>
        /// 当点击标题时
        /// </summary>
        public event EventHandler OnTitleClick = null;
        /// <summary>
        /// 当点击内容时
        /// </summary>
        public event EventHandler OnContentClick = null;
        #endregion

        #region Enums
        /// <summary>
        /// 通知栏的状态
        /// </summary>
        public enum MSNNotifierStates
        {
            Hidden = 0,
            Appearing = 1,
            Visible = 2,
            Disappearing = 3
        }
        #endregion

        #region 属性
        public Rectangle TitleContainer
        {
            get { return _titleRectangle; }
            set { _titleRectangle = value; }
        }
       
        public Rectangle ContentContainer
        {
            get { return _contentRectangle; }
            set { _contentRectangle = value; }
        }

        #region 显示属性
        /// <summary>
        /// 获取通知栏的状态
        /// </summary>
        public MSNNotifierStates NotifierState
        {
            get { return _notifierState; }
        }

        /// <summary>
        /// 获取或设置弹出窗体的标题
        /// </summary>
        public string Title
        {
            get { return _title; }
            set 
            {
                if (String.IsNullOrEmpty(value))
                    throw new ArgumentNullException("标题不能为空");
                _title = value;
                this.Refresh();
            }
        }

        /// <summary>
        /// 获取或设置弹出窗体的内容
        /// </summary>
        public string Content
        {
            get { return _content; }
            set 
            {
                if (String.IsNullOrEmpty(value))
                    throw new ArgumentNullException("内容不能为空");
                _content = value;
                this.Refresh();
            }
        }
        #endregion 

        #region 设计参数
        /// <summary>
        /// 标题的正常颜色
        /// </summary>
        public Color NormalTitleColor
        {
            get { return this._normalTitleColor; }
            set 
            {
                if (value == null)
                    throw new ArgumentNullException("标题颜色不能为空");
                this._normalTitleColor = value;
                this.Refresh();
            }
        }

        /// <summary>
        /// 鼠标悬停时标题的颜色
        /// </summary>
        public Color HoverTitleColor
        {
            get { return this._hoverTitleColor; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("鼠标悬停时标题的颜色不能为空");
                this._hoverTitleColor = value;
                this.Refresh();
            }
        }

        /// <summary>
        /// 内容的正常颜色
        /// </summary>
        public Color NormalContentColor
        {
            get { return this._normalContentColor; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("内容颜色不能为空");
                this._normalContentColor = value;
                this.Refresh();
            }
        }

        /// <summary>
        /// 鼠标悬停时内容的颜色
        /// </summary>
        public Color HoverContentColor
        {
            get { return this._hoverContentColor; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("鼠标悬停时内容的颜色不能为空");
                this._hoverContentColor = value;
                this.Refresh();
            }
        }

        /// <summary>
        /// 标题的字体
        /// </summary>
        public Font NormalTitleFont
        {
            get { return this._normalTitleFont; }
            set 
            {
                if (value == null)
                    throw new ArgumentNullException("参数不能为空");
                this._normalTitleFont = value;
            }
        }

        /// <summary>
        /// 鼠标悬停时标题的字体
        /// </summary>
        public Font HoverTitleFont
        {
            get { return this._hoverTitleFont; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("参数不能为空");
                this._hoverTitleFont = value;
            }
        }

        /// <summary>
        /// 内容的字体
        /// </summary>
        public Font NormalContentFont
        {
            get { return this._normalContentFont; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("参数不能为空");
                this._normalContentFont = value;
            }
        }

        /// <summary>
        /// 鼠标悬停时内容的字体
        /// </summary>
        public Font HoverContentFont
        {
            get { return this._hoverContentFont; }
            set
            {
                if (value == null)
                    throw new ArgumentNullException("参数不能为空");
                this._hoverContentFont = value;
            }
        }

        /// <summary>
        /// 代表透明的颜色
        /// </summary>
        protected Color TransparencyColor
        {
            get { return _transparencyColor; }
            set { _transparencyColor = value; }
        }

        /// <summary>
        /// 背景图片
        /// </summary>
        public Bitmap BackgroupBitmap
        {
            get 
            { 
                return this._background; 
            }
            set 
            {
                if (value == null)
                    throw new ArgumentNullException("背景图片不能为空");
                this._background = value;
                this.Width = _background.Width;
                this.Height = _background.Height;
                this.Region = BitmapToRegion(this._background, this._transparencyColor);
            }
        }

        /// <summary>
        /// 关闭按钮的位置
        /// </summary>
        protected Point CloseIconLocation
        {
            get { return _closeIconLocation; }
            set { _closeIconLocation = value; }
        }
        /// <summary>
        /// 关闭按钮图片
        /// </summary>
        public Bitmap CloseImage
        {
            get { return this._closeIcon; }
            set 
            {
                if (value == null)
                    throw new ArgumentNullException("关闭按钮图片不能为空");
                this._closeIcon = value;
                this._closeIcon.MakeTransparent(this._transparencyColor);
                this._closeIconSize = new Size(_closeIcon.Width / 3, _closeIcon.Height);
            }
        }
        #endregion

        #region 动作参数
        /// <summary>
        /// 显示时间(秒)
        /// </summary>
        public int TimeToShow
        {
            get { return _timeToShow; }
            set 
            {
                if (value <= 0)
                    throw new ArgumentException("参数必须大于0");
                _timeToShow = value; 
            }
        }
        /// <summary>
        /// 持续时间(秒)
        /// </summary>
        public int TimeToStay
        {
            get { return _timeToStay; }
            set
            {
                if (value <= 0)
                    throw new ArgumentException("参数必须大于0");
                _timeToStay = value;
            }
        }
        /// <summary>
        /// 消失时间(秒)
        /// </summary>
        public int TimeToHide
        {
            get { return _timeToHide; }
            set
            {
                if (value <= 0)
                    throw new ArgumentException("参数必须大于0"); 
                _timeToHide = value;
            }
        }
        /// <summary>
        /// 当鼠标至于窗体是否保存可视
        /// </summary>
        public bool KeepVisibleOnMouseOver
        {
            get { return _keepVisibleOnMouseOver; }
            set { _keepVisibleOnMouseOver = value; }
        }
        /// <summary>
        /// 当鼠标至于窗体是否重新显示
        /// </summary>
        public bool ReShowOnMouseOver
        {
            get { return _reShowOnMouseOver; }
            set { _reShowOnMouseOver = value; }
        }
        /// <summary>
        /// 标题是否可点击
        /// </summary>
        public bool TitleClickable
        {
            get { return _titleClickable; }
            set { _titleClickable = value; }
        }
        /// <summary>
        /// 内容是否可点击
        /// </summary>
        public bool ContentClickable
        {
            get { return _contentClickable; }
            set { _contentClickable = value; }
        }
        /// <summary>
        /// 当鼠标至于窗体是否保存可视
        /// </summary>
        public bool EnableSelectionRectangle
        {
            get { return _enableSelectionRectangle; }
            set { _enableSelectionRectangle = value; }
        }
        /// <summary>
        /// 关闭按钮是否可点击
        /// </summary>
        public bool CloseClickable
        {
            get { return _closeClickable; }
            set { _closeClickable = value; }
        }
        #endregion

        #endregion

        #region 外部方法
        /// <summary>
        /// 显示窗体
        /// </summary>
        /// <param name="hWnd">窗体的句柄</param>
        /// <param name="nCmdShow"></param>
        /// <returns>显示是否出现</returns>
        [DllImport("user32.dll")]
        private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);
        #endregion

        #region 公有方法
        /// <summary>
        /// 显示窗体
        /// </summary>
        public new void Show()
        {
            this._workAradRectangle = Screen.GetWorkingArea(this._workAradRectangle);

            _visibleEvents = _timeToStay;
            CalculateMouseRectangles();

            int events;
            //计算窗体显示的动画效果
            if (_timeToShow > 10)
            {
                events = Math.Min((_timeToShow / 10), _background.Height);
                _showEvents = _timeToShow / events;
                _incrementShow = _background.Height / events;
            }
            else
            {
                _showEvents = 10;
                _incrementShow = _background.Height;
            }

            //计算窗体消失的动画效果
            if (_timeToHide > 10)
            {
                events = Math.Min((_timeToHide / 10), _background.Height);
                _hideEvents = _timeToHide / events;
                _incrementHide = _background.Height / events;
            }
            else
            {
                _hideEvents = 10;
                _incrementHide = _background.Height;
            }

            //根据当前窗体的状态
            switch (_notifierState)
            {
                case MSNNotifierStates.Hidden:
                    _notifierState = MSNNotifierStates.Appearing;
                    SetBounds(_workAradRectangle.Right - _background.Width - 17, _workAradRectangle.Bottom - 1, _background.Width, 0);
                    timer.Interval = _showEvents;
                    timer.Start();
                    //显示窗体
                    ShowWindow(this.Handle, 4);
                    break;
                case MSNNotifierStates.Appearing:
                    Refresh();
                    break;
                case MSNNotifierStates.Visible:
                    timer.Stop();
                    timer.Interval = _visibleEvents;
                    timer.Start();
                    Refresh();
                    break;
                case MSNNotifierStates.Disappearing:
                    timer.Start();
                    _notifierState = MSNNotifierStates.Visible;
                    SetBounds(_workAradRectangle.Right - _background.Width - 17, _workAradRectangle.Bottom - _background.Height - 1, _background.Width, _background.Height);
                    timer.Interval = _visibleEvents;
                    timer.Start();
                    Refresh();
                    break;
            }
        }
        /// <summary>
        /// 隐藏窗体
        /// </summary>
        public new void Hide()
        {
            if (_notifierState != MSNNotifierStates.Hidden)
            {
                timer.Stop();
                _notifierState = MSNNotifierStates.Hidden;
                base.Hide(); 
            }
        }
        #endregion

        #region 保护方法
        protected void DrawCloseButton(Graphics grfx)
        {
            if (_closeIcon != null)
            {
                Rectangle rectDest = new Rectangle(_closeIconLocation, _closeIconSize);
                Rectangle rectSrc;

                if (_isMouseOverClose)
                {
                    if (!_isMouseDown)
                        rectSrc = new Rectangle(new Point(_closeIconSize.Width * 2, 0), _closeIconSize);
                    else
                        rectSrc = new Rectangle(new Point(_closeIconSize.Width, 0), _closeIconSize);
                }
                else
                    rectSrc = new Rectangle(new Point(0, 0), _closeIconSize);

                grfx.DrawImage(_closeIcon, rectDest, rectSrc, GraphicsUnit.Pixel);
            }
        }
        protected void DrawText(Graphics grfx)
        {
            if (_title != null && _title.Length != 0)
            {
                StringFormat sf = new StringFormat();
                sf.Alignment = StringAlignment.Near;
                sf.LineAlignment = StringAlignment.Center;
                sf.FormatFlags = StringFormatFlags.NoWrap;
                sf.Trimming = StringTrimming.EllipsisCharacter;
                if (_isMouseOverTitle)
                    grfx.DrawString(_title, _hoverTitleFont, new SolidBrush(_hoverTitleColor), _titleRectangle, sf);
                else
                    grfx.DrawString(_title, _normalTitleFont, new SolidBrush(_normalTitleColor), _titleRectangle, sf);
            }

            if (_content != null && _content.Length != 0)
            {
                StringFormat sf = new StringFormat();
                sf.Alignment = StringAlignment.Center;
                sf.LineAlignment = StringAlignment.Center;
                sf.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
                sf.Trimming = StringTrimming.Word;							// Added Rev 002

                if (_isMouseOverContent)
                {
                    grfx.DrawString(_content, _hoverContentFont, new SolidBrush(_hoverContentColor), _contentRectangle, sf);
                    if (_enableSelectionRectangle)
                        ControlPaint.DrawBorder3D(grfx, _realContentRectangle, Border3DStyle.Etched, Border3DSide.Top | Border3DSide.Bottom | Border3DSide.Left | Border3DSide.Right);

                }
                else
                    grfx.DrawString(_content, _normalContentFont, new SolidBrush(_normalContentColor), _contentRectangle, sf);
            }
        }
        /// <summary>
        /// 计算鼠标可以取得的窗体范围
        /// </summary>
        protected void CalculateMouseRectangles()
        {
            Graphics grfx = CreateGraphics();
            StringFormat sf = new StringFormat();
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;
            sf.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;

            SizeF sizefTitle = grfx.MeasureString(this._title, this._hoverTitleFont, this._titleRectangle.Width, sf);
            SizeF sizefContent = grfx.MeasureString(this._content, this._hoverContentFont, this._contentRectangle.Width, sf);
            grfx.Dispose();

            //确定真实的标题区域
            if (sizefTitle.Height > _titleRectangle.Height)
                _realTitleRectangle = new Rectangle(_titleRectangle.Left, _titleRectangle.Top, _titleRectangle.Width, _titleRectangle.Height);
            else
                _realTitleRectangle = new Rectangle(_titleRectangle.Left, _titleRectangle.Top, (int)sizefTitle.Width, (int)sizefTitle.Height);
            _realTitleRectangle.Inflate(0, 2);

            //确定真实的内容区域
            if (sizefContent.Height > _contentRectangle.Height)
                _realContentRectangle = new Rectangle((_contentRectangle.Width - (int)sizefContent.Width) / 2 + _contentRectangle.Left, _contentRectangle.Top, (int)sizefContent.Width, (int)_contentRectangle.Height);
            else
                _realContentRectangle = new Rectangle((_contentRectangle.Width - (int)sizefContent.Width) / 2 + _contentRectangle.Left, (_contentRectangle.Height - (int)sizefContent.Height) / 2 + _contentRectangle.Top, (int)sizefContent.Width, (int)_contentRectangle.Height);
        }
        /// <summary>
        /// 将位图绘制到区域中
        /// </summary>
        /// <param name="bitmap">位图</param>
        /// <param name="transparencyColor">透明色</param>
        protected Region BitmapToRegion(Bitmap bitmap, Color transparencyColor)
        {
            if (bitmap == null)
                throw new ArgumentNullException("bitmap", "输入的位图不能为空");

            int height = bitmap.Height;
            int width = bitmap.Width;

            GraphicsPath path = new GraphicsPath();

            for (int j = 0; j < height; j++)
            {
                for (int i = 0; i < width; i++)
                {
                    if (bitmap.GetPixel(i, j) == transparencyColor)
                        continue;
                    int x0 = i;
                    while ((i < width) && (bitmap.GetPixel(i, j) != transparencyColor))
                        i++;
                    path.AddRectangle(new Rectangle(x0, j, i - x0, 1));
                }
            }
            System.Drawing.Region region = new Region(path);
            path.Dispose();
            return region;
        }

        /// <summary>
        /// 显示效果计时器的回调函数
        /// </summary>
        protected void TimerCallback(object obj, EventArgs e)
        {
            switch (this._notifierState)
            {
                case MSNNotifierStates.Appearing:
                    if (Height < _background.Height)
                        SetBounds(Left, Top - _incrementShow, Width, Height + _incrementShow);
                    else
                    {
                        timer.Stop();
                        Height = _background.Height;
                        timer.Interval = _visibleEvents;
                        _notifierState = MSNNotifierStates.Visible;
                        timer.Start();
                    }
                    break;
                case MSNNotifierStates.Visible:
                    timer.Stop();
                    timer.Interval = _hideEvents;
                    if ((_keepVisibleOnMouseOver && !_isMouseOverPopup) || (!_keepVisibleOnMouseOver))
                        _notifierState = MSNNotifierStates.Disappearing;
                    timer.Start();
                    break;
                case MSNNotifierStates.Disappearing:
                    if (_reShowOnMouseOver && _isMouseOverPopup)
                        _notifierState = MSNNotifierStates.Appearing;
                    else
                    {
                        if (Top < _workAradRectangle.Bottom)
                            SetBounds(Left, Top + _incrementHide, Width, Height - _incrementHide);
                        else
                            Hide();
                    }
                    break;
            }
        }
        #endregion

        #region 重写函数
        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            _isMouseOverPopup = true;
            Refresh();
        }
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            _isMouseOverPopup = false;
            _isMouseOverClose = false;
            _isMouseOverTitle = false;
            _isMouseOverContent = false;
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            bool contentModified = false;

            if ((e.X > _closeIconLocation.X) && (e.X < _closeIconLocation.X + _closeIconSize.Width) && (e.Y > _closeIconLocation.Y) && (e.Y < _closeIconLocation.Y + _closeIconSize.Height) && _closeClickable)
            {
                if (!_isMouseOverClose)
                {
                    _isMouseOverClose = true;
                    _isMouseOverTitle = false;
                    _isMouseOverContent = false;
                    Cursor = Cursors.Hand;
                    contentModified = true;
                }
            }
            else if (_realContentRectangle.Contains(new Point(e.X, e.Y)) && _contentClickable)
            {
                if (!_isMouseOverClose)
                {
                    _isMouseOverClose = false;
                    _isMouseOverTitle = false;
                    _isMouseOverContent = true;
                    Cursor = Cursors.Hand;
                    contentModified = true;
                }
            }
            else if (_realTitleRectangle.Contains(new Point(e.X, e.Y)) && _titleClickable)
            {
                if (!_isMouseOverClose)
                {
                    _isMouseOverClose = false;
                    _isMouseOverTitle = true;
                    _isMouseOverContent = false;
                    Cursor = Cursors.Hand;
                    contentModified = true;
                }
            }
            else
            {
                if (_isMouseOverClose || _isMouseOverTitle || _isMouseOverContent)
                    contentModified = true;

                _isMouseOverClose = false;
                _isMouseOverTitle = false;
                _isMouseOverContent = false;
                Cursor = Cursors.Default;
            }

            if (contentModified)
                Refresh();
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            _isMouseDown = true;

            if (_isMouseOverClose)
                Refresh();
        }
        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            _isMouseDown = false;

            if (_isMouseOverClose)
            {
                Hide();
                if (OnCloseClick != null)
                    OnCloseClick(this, new EventArgs());
            }
            else if (_isMouseOverTitle)
            {
                if (OnTitleClick != null)
                    OnTitleClick(this, new EventArgs());
            }
            else if (_isMouseOverContent)
            {
                if (OnContentClick != null)
                    OnContentClick(this, new EventArgs());
            }
        }
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            Graphics grfx = e.Graphics;
            grfx.PageUnit = GraphicsUnit.Pixel;

            Graphics offScreenGraphics;
            Bitmap offscreenBitmap;

            offscreenBitmap = new Bitmap(_background.Width, _background.Height);
            offScreenGraphics = Graphics.FromImage(offscreenBitmap);

            if (_background != null)
            {
                offScreenGraphics.DrawImage(_background, 0, 0, _background.Width, _background.Height);
            }

            DrawCloseButton(offScreenGraphics);
            DrawText(offScreenGraphics);

            grfx.DrawImage(offscreenBitmap, 0, 0);
        }
        #endregion
    }
}
